﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace CodeAnalysisRuleCollection.Common.CSharp
{
    /// <example>public const PI = 3.14;</example>
    [ExportCodeRefactoringProvider("ConvertConstantToReadOnlyField", LanguageNames.CSharp)]
    internal class ConvertConstantToReadOnlyField : ICodeRefactoringProvider
    {
        public async Task<IEnumerable<CodeAction>> GetRefactoringsAsync(Document document, TextSpan span, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            // find field declaration
            var field = root.FindToken(span.Start).Parent.FirstAncestorOrSelf<FieldDeclarationSyntax>();
            if (field == null || !field.Modifiers.Any(SyntaxKind.ConstKeyword))
                return null;

            return new[] {
                CodeAction.Create("Convert constant to read-only field", c => ConvertConstantToReadOnlyFieldAsync(root, document, field, c)),
                CodeAction.Create("Convert constant to read-only field (static)", c => ConvertConstantToStaticReadOnlyFieldAsync(root, document, field, c))
            };
        }

        private async Task<Document> ConvertConstantToReadOnlyFieldAsync(SyntaxNode root, Document document, FieldDeclarationSyntax field, CancellationToken c)
        {
            var newRoot = root.ReplaceNode(
                field,
                field.WithModifiers(
                    field.Modifiers.Replace(
                        field.Modifiers.First(t => t.IsKind(SyntaxKind.ConstKeyword)),
                        SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)
                    )
                )
                    .WithAdditionalAnnotations(Formatter.Annotation)
            );

            return document.WithSyntaxRoot(newRoot);
        }

        private async Task<Document> ConvertConstantToStaticReadOnlyFieldAsync(SyntaxNode root, Document document, FieldDeclarationSyntax field, CancellationToken c)
        {
            var newRoot = root.ReplaceNode(
                field,
                field.WithModifiers(
                    field.Modifiers.ReplaceRange(
                        field.Modifiers.First(t => t.IsKind(SyntaxKind.ConstKeyword)),
                        new[] { SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword) }
                    )
                )
                    .WithAdditionalAnnotations(Formatter.Annotation)
            );

            return document.WithSyntaxRoot(newRoot);
        }
    }
}
